Aquesta és la llibreta on s’integraran tots els apartats que anem
treballant durant el curs. Començarem amb una caracterització de la
demanda històrica d’un conjunt d’usuaris fictícis d’una comunitat
energètica i durant les properes setmanes anirem ampliant les
funcionalitats de la llibreta per tal de cobrir tot el que us anem
explicant durant el curs.
1. Demanda energètica d’una comunitat
Durant aquesta part del curs obtenim les dades de consum d’un seguit
d’usuaris a través de la plataforma Datadis. Recordeu que us hem explicat
com aconseguir credencials i descarregar informació d’aquesta plataforma
en el seguent enllaç
al Notion.
La metodología que utilitzarem per a que la llibreta interpreti
correctament l’històric de demanda energètica dels usuaris de la
comunitat és la seguent:
- A través del vostre navegador web preferit, descarregar tots aquells
punts de consum (CUPS) que formin part de la comunitat energètica a
través de la plataforma Datadis. Descarregarem tant històric de consum
com hi hagi disponible. És important que com a mínim tinguem un any
complet per cada usuari.
- Guardar cada fitxer amb el nom del NIF relacionat, seguit d’un guió
baix i un número consecutiu per tal de tenir un nom únic per cada
fitxer. Per exemple, en el cas d’un usuari amb NIF 43311022D generariem
els seguents fitxers 43311022D_1.csv, 43311022D_2.csv, 43311022D_3.csv
…
- Moure tots els fitxers descarregats i renombrats a dins de la
carpeta Dades/Datadis que podem trobar en el directori de la
llibreta.
S'han trobat 11 fitxers amb dades provinents de Datadis.
if(exists_demand_data) {
# Llegim tots els fitxers de dades de Datadis
df <- read_datadis_files(datadis_files,only_real_values = F)
df <- df %>%
filter(is.finite(time) &
!duplicated(df[,c("time","NIF","CUPS")]))
}
if(exists_demand_data) {
# Llegim tots els fitxers de dades de E-Distribución
df_edist <- read_edistribucion_files(edistribucion_files,only_real_values = F)
df_edist <- df_edist %>%
filter(is.finite(time) &
!duplicated(df_edist[,c("time","NIF","CUPS")]))
df <- rbind(df, df_edist)
}
if(exists_demand_data) {
df_NIF <- df %>%
group_by(time,CUPS) %>%
summarise(
consumption=sum(consumption),
NIF=first(NIF)
) %>%
ungroup() %>%
select(-CUPS)
}
1.1 Agregació de demanda hora a hora
El potencial autoconsum col·lectiu que es pot fer en una comunitat
energètica radica principalment en la complementarietat de les corbes de
càrrega individuals. I en la possibilitat, en la mesura del possible, de
moure consums en els moments en que l’energia produïda és superior a la
demandada, ja que sinó es generen excedents.
Primerament, si tenim en compte l´últim any de dades disponibles per
cadascún dels CUPS que integren la comunitat energètica, tenim la
seguent corba de càrrega agregada:
if(exists_demand_data) {
df_ly <- df_NIF %>%
group_by(ytime= format(time,"%m-%d %H:%M"), NIF) %>%
summarise(
consumption=mean(consumption,na.rm=T)
) %>%
mutate(
ytime = as.POSIXct(paste0("2020-",ytime),format = "%Y-%m-%d %H:%M",
tz="Europe/Madrid")
) %>%
filter( is.finite(ytime) ) %>%
ungroup()
}
1.2 Agregació de demanda mitja per estació de l’any
Com es pot veure, l’agregació anterior ens permet anar a un detall
molt precís sobre quina demanda agregada hi hagués hagut a cada hora i
quina repartició entre els diferents usuaris. Tot i això, per entendre
una mica més el de esón una mica complicats de dig
if(exists_demand_data) {
df_ly_season <- df_NIF %>%
mutate(
yday = as.numeric(format(time,"%j")),
season = ifelse(yday<79 | yday>=355, "Hivern",
ifelse(yday>=79 & yday<172, "Primavera",
ifelse(yday>=172 & yday<266,"Estiu",
"Tardor"))),
month = month(time)
) %>%
group_by(hour = sprintf("%02i:%02i",hour(time),minute(time)),
season, NIF) %>%
summarise(
# date = as.Date(paste0(format(time,"%Y-%m"),"-01")),
date = ifelse(season=="Hivern",as.Date("2021-02-01"),
ifelse(season=="Primavera",as.Date("2021-05-01"),
ifelse(season=="Estiu",as.Date("2021-08-01"),
ifelse(season=="Tardor",as.Date("2021-11-01"))))),
consumption=mean(consumption)
) %>%
mutate(
time = as.POSIXct(paste(as.Date(date,origin=as.Date("1970-01-01")),hour))
) %>% ungroup() %>%
select(-hour, -date)
}
Mapa de calor anual de la demanda agregada
Una manera alternativa i que ens proporciona molta informació en un
cop de vista al respecte de la demanda energètica agregada són els mapes
de calor.
df_ly_total <- df_ly %>%
group_by(ytime) %>%
summarise(
consumption = mean(consumption)*sum(is.finite(consumption))
) %>%
ungroup() %>%
mutate(year = year(ytime),
month = month(ytime, label=TRUE),
day = day(ytime),
hour = hour(ytime)) %>%
select(ytime,day,hour,month,year,consumption) %>%
fill(consumption)

2. Generació fotovoltàica
Segons els escenaris definits en els fitxers JSON (definit, per
exemple a: Dades/PVGIS/escenaris.json), s’estima quina és la producció
FotoVoltaica (FV) de cadascún dels camps definits. Cadascún d’ells anirà
definit per un nom que ha de ser únic. A posteriori, el programa que
genera la llibreta ja calcula quina és la generació agregada de tots els
camps, és a dir, poden definir-se diferents tipologies de camps FV, amb
canvis de posicions i/o característiques en cadascún d’ells.
if(length(pvgis_files)==0){
generation_df <- NULL
"No s'ha definit cap camp fotovoltaic! Genereu un fitxer JSON com a l'exemple"
} else {
pvgis_scenarios <- do.call(c,lapply(pvgis_files,
function(x)fromJSON(x,simplifyDataFrame = F)))
generation_df <- pvgis_download_scenarios(pvgis_scenarios)
}
Seguidament, visualitzem la producció FV de cadascún dels camps que
están definits en el fitxer Dades/PVGIS/escenaris.json
3. Comprovació del nivell d’autoconsum
if(!is.null(generation_df)){
p <- dygraph(
xts(df_ly_total_with_gen %>% select(surplus, selfcons, fromgrid) %>%
rename("Excedents"="surplus","Autoconsum"="selfcons",
"XarxaElèctrica"="fromgrid"),
order.by = df_ly_total_with_gen$ytime),
main = paste0("Excedents, autoconsum solar (",
round(sum(df_ly_total_with_gen$selfcons,na.rm=T)/
sum(df_ly_total_with_gen$generation,na.rm=T),3)*100,
"%) i consum de xarxa respecte total (",
round(-sum(df_ly_total_with_gen$fromgrid,na.rm=T)/
sum(df_ly_total_with_gen$consumption,na.rm=T),3)*100,"%)"),
width = 900,
height = 500
) %>%
dyOptions(stackedGraph = F, connectSeparatedPoints = T,
fillGraph=T, drawGrid = F) %>%
dyLegend(show = "always") %>%
dyRangeSelector(height = 30) %>%
dyAxis("x", label = "temps") %>%
dyAxis("y", label = "electricitat (kWh)") %>%
dyHighlight(highlightCircleSize = 2.5,
highlightSeriesBackgroundAlpha = 0.9,
hideOnMouseOut = T) %>%
dyRoller(rollPeriod = 1) %>%
dyCrosshair(direction = "vertical")
htmltools::tagList(
list(htmltools::tags$div(p, style = "padding:10px; border: solid"))
)
}
LS0tCnRpdGxlOiBBbsOgbGlzaSBkZSBkYWRlcyBlbiBjb211bml0YXRzIGVuZXJnw6h0aXF1ZXMKc3VidGl0bGU6IElNTyBTYWx2YWRvciBTZWd1w60gLSBHZXN0acOzIGRlIGNvbXVuaXRhdHMgZW5lcmfDqHRpcXVlcyAtIDIwMjIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICBkZl9wcmludDogcGFnZWQKLS0tCgoqKioKCkFxdWVzdGEgw6lzIGxhIGxsaWJyZXRhIG9uIHMnaW50ZWdyYXJhbiB0b3RzIGVscyBhcGFydGF0cyBxdWUgYW5lbSB0cmViYWxsYW50IGR1cmFudCBlbCBjdXJzLiBDb21lbsOnYXJlbSBhbWIgdW5hIGNhcmFjdGVyaXR6YWNpw7MgZGUgbGEgZGVtYW5kYSBoaXN0w7JyaWNhIGQndW4gY29uanVudCBkJ3VzdWFyaXMgZmljdMOtY2lzIGQndW5hIGNvbXVuaXRhdCBlbmVyZ8OodGljYSBpIGR1cmFudCBsZXMgcHJvcGVyZXMgc2V0bWFuZXMgYW5pcmVtIGFtcGxpYW50IGxlcyBmdW5jaW9uYWxpdGF0cyBkZSBsYSBsbGlicmV0YSBwZXIgdGFsIGRlIGNvYnJpciB0b3QgZWwgcXVlIHVzIGFuZW0gZXhwbGljYW50IGR1cmFudCBlbCBjdXJzLiAKCioqQVRFTkNJw5MhKiogVGluZ3VldSBlbiBjb21wdGUgcXVlIHBvZHJldSByZWdlbmVyYXIgZWxzIHJlc3VsdGF0cyBhbWIgZWxzIGZpdHhlcnMgZGUgZGFkZXMgcXVlIHZ1bGd1ZXUgbyBuZWNlc3NpdGV1LiBQZXIgdGFudCwgYXF1ZXN0YSBlaW5hIHVzIHBvZHLDoCBzZXIgZCd1dGlsaXRhdCB1biBjb3AgY29tZW5jZXUgYSB0cmViYWxsYXIuIAoKYGBge3IsIGVjaG89VCwgcmVzdWx0cz1GLCBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0KbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGRhdGEudGFibGUpCmxpYnJhcnkobHVicmlkYXRlKQpsaWJyYXJ5KHBsb3RseSkKbGlicmFyeShkeWdyYXBocykKbGlicmFyeSh0aWR5cikKbGlicmFyeShodG1sdG9vbHMpCmxpYnJhcnkoeHRzKQpsaWJyYXJ5KHZpcmlkaXMpCmxpYnJhcnkoZ2dFeHRyYSkKbGlicmFyeShkdXRpbHMpCmxpYnJhcnkoanNvbmxpdGUpCmxpYnJhcnkoaHR0cikKbGlicmFyeShkcGx5cikKbGlicmFyeShvY2UpCmxpYnJhcnkocGFyc2VkYXRlKQpzb3VyY2UoImZ1bmNpb25zLlIiKQpgYGAKKioqCgojIDEuIERlbWFuZGEgZW5lcmfDqHRpY2EgZCd1bmEgY29tdW5pdGF0CgpEdXJhbnQgYXF1ZXN0YSBwYXJ0IGRlbCBjdXJzIG9idGVuaW0gbGVzIGRhZGVzIGRlIGNvbnN1bSBkJ3VuIHNlZ3VpdCBkJ3VzdWFyaXMgYSB0cmF2w6lzIGRlIGxhIHBsYXRhZm9ybWEgW0RhdGFkaXNdKGh0dHBzOjovL3d3dy5kYXRhZGlzLmVzKS4gUmVjb3JkZXUgcXVlIHVzIGhlbSBleHBsaWNhdCBjb20gYWNvbnNlZ3VpciBjcmVkZW5jaWFscyBpIGRlc2NhcnJlZ2FyIGluZm9ybWFjacOzIGQnYXF1ZXN0YSBwbGF0YWZvcm1hIGVuIGVsIHNlZ3VlbnQgW2VubGxhw6cgYWwgTm90aW9uXShodHRwczovL3N1bXB0dW91cy1wdXBweS1iODYubm90aW9uLnNpdGUvT2J0ZW5pci1jcmVkZW5jaWFscy1pLWRhZGVzLWRlLURhdGFkaXMtMWM4NzUxNmMxMWY3NDBkZjhiNzc1YWFiYTg4ZGI1NmUpLgoKTGEgbWV0b2RvbG9nw61hIHF1ZSB1dGlsaXR6YXJlbSBwZXIgYSBxdWUgbGEgbGxpYnJldGEgaW50ZXJwcmV0aSBjb3JyZWN0YW1lbnQgbCdoaXN0w7JyaWMgZGUgZGVtYW5kYSBlbmVyZ8OodGljYSBkZWxzIHVzdWFyaXMgZGUgbGEgY29tdW5pdGF0IMOpcyBsYSBzZWd1ZW50OgoKMS4gQSB0cmF2w6lzIGRlbCB2b3N0cmUgbmF2ZWdhZG9yIHdlYiBwcmVmZXJpdCwgZGVzY2FycmVnYXIgdG90cyBhcXVlbGxzIHB1bnRzIGRlIGNvbnN1bSAoQ1VQUykgcXVlIGZvcm1pbiBwYXJ0IGRlIGxhIGNvbXVuaXRhdCBlbmVyZ8OodGljYSBhIHRyYXbDqXMgZGUgbGEgcGxhdGFmb3JtYSBEYXRhZGlzLiBEZXNjYXJyZWdhcmVtIHRhbnQgaGlzdMOycmljIGRlIGNvbnN1bSBjb20gaGkgaGFnaSBkaXNwb25pYmxlLiDDiXMgaW1wb3J0YW50IHF1ZSBjb20gYSBtw61uaW0gdGluZ3VlbSB1biBhbnkgY29tcGxldCBwZXIgY2FkYSB1c3VhcmkuCjIuIEd1YXJkYXIgY2FkYSBmaXR4ZXIgYW1iIGVsIG5vbSBkZWwgTklGIHJlbGFjaW9uYXQsIHNlZ3VpdCBkJ3VuIGd1acOzIGJhaXggaSB1biBuw7ptZXJvIGNvbnNlY3V0aXUgcGVyIHRhbCBkZSB0ZW5pciB1biBub20gw7puaWMgcGVyIGNhZGEgZml0eGVyLiBQZXIgZXhlbXBsZSwgZW4gZWwgY2FzIGQndW4gdXN1YXJpIGFtYiBOSUYgNDMzMTEwMjJEIGdlbmVyYXJpZW0gZWxzIHNlZ3VlbnRzIGZpdHhlcnMgNDMzMTEwMjJEXzEuY3N2LCA0MzMxMTAyMkRfMi5jc3YsIDQzMzExMDIyRF8zLmNzdiAuLi4KMy4gTW91cmUgdG90cyBlbHMgZml0eGVycyBkZXNjYXJyZWdhdHMgaSByZW5vbWJyYXRzIGEgZGlucyBkZSBsYSBjYXJwZXRhIERhZGVzL0RhdGFkaXMgcXVlIHBvZGVtIHRyb2JhciBlbiBlbCBkaXJlY3RvcmkgZGUgbGEgbGxpYnJldGEuCgoKYGBge3IsIGVjaG89RiwgcmVzdWx0cz1GfQp3ZCA8LSBub3JtYWxpemVQYXRoKCIuIikKZGlyLmNyZWF0ZShwYXN0ZTAod2QsIi8uLi9EYWRlcyIpLCBzaG93V2FybmluZ3MgPSBGKQpkaXIuY3JlYXRlKHBhc3RlMCh3ZCwiLy4uL0RhZGVzL0RhdGFkaXMiKSxzaG93V2FybmluZ3MgPSBGKQpkaXIuY3JlYXRlKHBhc3RlMCh3ZCwiLy4uL0RhZGVzL1BWR0lTIiksc2hvd1dhcm5pbmdzID0gRikKZGlyLmNyZWF0ZShwYXN0ZTAod2QsIi8uLi9EYWRlcy9lLWRpc3RyaWJ1Y2nDs24iKSxzaG93V2FybmluZ3MgPSBGKQoKZGF0YWRpc19maWxlcyA8LSBsaXN0LmZpbGVzKHBhc3RlMCh3ZCwiLy4uL0RhZGVzL0RhdGFkaXMiKSxmdWxsLm5hbWVzID0gVCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhdHRlcm4gPSAiLmNzdiQiKQplZGlzdHJpYnVjaW9uX2ZpbGVzIDwtIGxpc3QuZmlsZXMocGFzdGUwKHdkLCIvLi4vRGFkZXMvZS1kaXN0cmlidWNpw7NuIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmdWxsLm5hbWVzID0gVCxwYXR0ZXJuID0gIi5jc3YkIikKcHZnaXNfZmlsZXMgPC0gbGlzdC5maWxlcyhwYXN0ZTAod2QsIi8uLi9EYWRlcy9QVkdJUyIpLGZ1bGwubmFtZXMgPSBULAogICAgICAgICAgICAgICAgICAgICAgICAgIHBhdHRlcm4gPSAiLmpzb24kIikKCmBgYApgYGB7ciwgZWNobz1GLCByZXN1bHRzPUZ9CmV4aXN0c19kZW1hbmRfZGF0YSA8LSBsZW5ndGgoZGF0YWRpc19maWxlcykgPiAwCgppZighZXhpc3RzX2RlbWFuZF9kYXRhKSB7CiAgbWVzc2FnZSgiQWN0dWFsbWVudCBubyBleGlzdGVpeGVuIGZpdHhlcnMgZGUgZGVtYW5kYSBlbmVyZ8OodGljYS4gCiAgICAgICAgICBTaSB1cyBwbGF1LCBhZmVnaXUtbidoaSBpIGV4ZWN1dGV1IGxhIGxsaWJyZXRhIGRlIG5vdS4iKQp9IGVsc2UgewogIG1lc3NhZ2Uoc3ByaW50ZigiUydoYW4gdHJvYmF0ICVzIGZpdHhlcnMgYW1iIGRhZGVzIHByb3ZpbmVudHMgZGUgRGF0YWRpcy4iLAogICAgICAgICAgICAgICAgICBsZW5ndGgoZGF0YWRpc19maWxlcykpKQp9CmBgYAoKYGBge3IsIGVjaG89VCwgcmVzdWx0cz1GLCBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0KaWYoZXhpc3RzX2RlbWFuZF9kYXRhKSB7CiAgCiAgIyBMbGVnaW0gdG90cyBlbHMgZml0eGVycyBkZSBkYWRlcyBkZSBEYXRhZGlzCiAgZGYgPC0gcmVhZF9kYXRhZGlzX2ZpbGVzKGRhdGFkaXNfZmlsZXMsb25seV9yZWFsX3ZhbHVlcyA9IEYpCiAgZGYgPC0gZGYgJT4lIAogICAgZmlsdGVyKGlzLmZpbml0ZSh0aW1lKSAmCiAgICAgICAgICAgIWR1cGxpY2F0ZWQoZGZbLGMoInRpbWUiLCJOSUYiLCJDVVBTIildKSkKfQpgYGAKCmBgYHtyLCBlY2hvPVQsIHJlc3VsdHM9RiwgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9CmlmKGV4aXN0c19kZW1hbmRfZGF0YSkgewogIAogICMgTGxlZ2ltIHRvdHMgZWxzIGZpdHhlcnMgZGUgZGFkZXMgZGUgRS1EaXN0cmlidWNpw7NuCiAgZGZfZWRpc3QgPC0gcmVhZF9lZGlzdHJpYnVjaW9uX2ZpbGVzKGVkaXN0cmlidWNpb25fZmlsZXMsb25seV9yZWFsX3ZhbHVlcyA9IEYpCiAgZGZfZWRpc3QgPC0gZGZfZWRpc3QgJT4lIAogICAgZmlsdGVyKGlzLmZpbml0ZSh0aW1lKSAmCiAgICAgICAgICAgIWR1cGxpY2F0ZWQoZGZfZWRpc3RbLGMoInRpbWUiLCJOSUYiLCJDVVBTIildKSkKICBkZiA8LSByYmluZChkZiwgZGZfZWRpc3QpCn0KYGBgCgo8Y2VudGVyPgpgYGB7ciwgZWNobz1GLCByZXN1bHRzPVQsIG1lc3NhZ2U9Riwgd2FybmluZz1GLCBvdXQud2lkdGg9IjEwMCUifQppZihleGlzdHNfZGVtYW5kX2RhdGEpIHsKICAjIFZpc3VhbGl0emVtIGxlcyBjb3JiZXMgZGUgY8OgcnJlZ2EgaW5kaXZpZHVhbG1lbnQgcGVyIE5JRiwgdXRpbGl0emFudCB1biBncsOgZmljIGludGVyYWN0aXUKICAjIGdncGxvdGx5KAogICMgICBnZ3Bsb3QoZGYpICsgCiAgIyAgICAgZ2VvbV9saW5lKGFlcyh0aW1lLGNvbnN1bXB0aW9uLGdyb3VwPUNVUFMsY29sPU5JRiksYWxwaGE9MC42KSArCiAgIyAgICAgeGxhYigidGVtcHMiKSArIHlsYWIoImNvbnN1bSAoa1doKSIpICsgCiAgIyAgICAgZ2d0aXRsZSgiQ29yYmVzIGRlIGPDoHJyZWdhIGluZGl2aWR1YWxzIHBlciBOSUYiKSArIHRoZW1lX2J3KCkKICAjICkKICBwdHMgPC0gbGFwcGx5KHVuaXF1ZShkZiRDVVBTKSxmdW5jdGlvbihpKXsKICAgIGF1eCA8LSBkZiAlPiUgZmlsdGVyKENVUFM9PWkpCiAgICBwIDwtIGR5Z3JhcGgoeHRzKHggPSBkYXRhLmZyYW1lKHZhbG9yPWF1eCRjb25zdW1wdGlvbiksIAogICAgICAgICAgICAgICAgb3JkZXIuYnkgPSBhdXgkdGltZSksIAogICAgICAgICAgICBtYWluPXNwcmludGYoIk5JRjogJXMsIENVUFM6ICVzIixkZiROSUZbZGYkQ1VQUz09aV1bMV0saSksCiAgICAgICAgICAgIHdpZHRoID0gODAwLCBoZWlnaHQ9MzUwKSAlPiUKICAgICAgZHlPcHRpb25zKGNvbm5lY3RTZXBhcmF0ZWRQb2ludHMgPSBULCBmaWxsR3JhcGg9VCwgZmlsbEFscGhhPTAuNCwgCiAgICAgICAgICAgICAgICBkcmF3R3JpZCA9IEYsIGNvbG9ycz0iI0Q4QUU1QSIpICAlPiUKICAgICAgZHlSYW5nZVNlbGVjdG9yKGhlaWdodCA9IDMwKSAlPiUKICAgICAgZHlBeGlzKCJ4IiwgbGFiZWwgPSAidGVtcHMiKSAlPiUKICAgICAgZHlBeGlzKCJ5IiwgbGFiZWwgPSAiY29uc3VtIChrV2gpIikgJT4lCiAgICBkeUhpZ2hsaWdodChoaWdobGlnaHRDaXJjbGVTaXplID0gMi41LAogICAgICAgICAgICAgICAgaGlnaGxpZ2h0U2VyaWVzQmFja2dyb3VuZEFscGhhID0gMC44LAogICAgICAgICAgICAgICAgaGlkZU9uTW91c2VPdXQgPSBUKSAlPiUKICAgIGR5Um9sbGVyKHJvbGxQZXJpb2QgPSAxKSAlPiUgCiAgICBkeUNyb3NzaGFpcihkaXJlY3Rpb24gPSAidmVydGljYWwiKQogICAgIGh0bWx0b29sczo6dGFncyRkaXYocCwgc3R5bGUgPSAicGFkZGluZzoxMHB4OyBib3JkZXI6IHNvbGlkIikKICB9KQogIGh0bWx0b29sczo6dGFnTGlzdChwdHMpCn0KYGBgCjwvY2VudGVyPgoKYGBge3IsIGVjaG89VCwgcmVzdWx0cz1GLCB3YXJuaW5nPUYsIG1lc3NhZ2U9Rn0KaWYoZXhpc3RzX2RlbWFuZF9kYXRhKSB7CiAgZGZfTklGIDwtIGRmICU+JSAKICAgIGdyb3VwX2J5KHRpbWUsQ1VQUykgJT4lCiAgICBzdW1tYXJpc2UoCiAgICAgIGNvbnN1bXB0aW9uPXN1bShjb25zdW1wdGlvbiksCiAgICAgIE5JRj1maXJzdChOSUYpCiAgICApICU+JQogICAgdW5ncm91cCgpICU+JQogICAgc2VsZWN0KC1DVVBTKQp9CmBgYAoKCioqKgoKIyMjIDEuMSBBZ3JlZ2FjacOzIGRlIGRlbWFuZGEgaG9yYSBhIGhvcmEKCgpFbCBwb3RlbmNpYWwgYXV0b2NvbnN1bSBjb2zCt2xlY3RpdSBxdWUgZXMgcG90IGZlciBlbiB1bmEgY29tdW5pdGF0IGVuZXJnw6h0aWNhIHJhZGljYSBwcmluY2lwYWxtZW50IGVuIGxhIGNvbXBsZW1lbnRhcmlldGF0IGRlIGxlcyBjb3JiZXMgZGUgY8OgcnJlZ2EgaW5kaXZpZHVhbHMuIEkgZW4gbGEgcG9zc2liaWxpdGF0LCBlbiBsYSBtZXN1cmEgZGVsIHBvc3NpYmxlLCBkZSBtb3VyZSBjb25zdW1zIGVuIGVscyBtb21lbnRzIGVuIHF1ZSBsJ2VuZXJnaWEgcHJvZHXDr2RhIMOpcyBzdXBlcmlvciBhIGxhIGRlbWFuZGFkYSwgamEgcXVlIHNpbsOzIGVzIGdlbmVyZW4gZXhjZWRlbnRzLgoKUHJpbWVyYW1lbnQsIHNpIHRlbmltIGVuIGNvbXB0ZSBswrTDumx0aW0gYW55IGRlIGRhZGVzIGRpc3BvbmlibGVzIHBlciBjYWRhc2PDum4gZGVscyBDVVBTIHF1ZSBpbnRlZ3JlbiBsYSBjb211bml0YXQgZW5lcmfDqHRpY2EsIHRlbmltIGxhIHNlZ3VlbnQgY29yYmEgZGUgY8OgcnJlZ2EgYWdyZWdhZGE6CgpgYGB7ciwgZWNobz1ULCByZXN1bHRzPUYsIHdhcm5pbmc9RiwgbWVzc2FnZT1GfQppZihleGlzdHNfZGVtYW5kX2RhdGEpIHsKICBkZl9seSA8LSBkZl9OSUYgJT4lCiAgICBncm91cF9ieSh5dGltZT0gZm9ybWF0KHRpbWUsIiVtLSVkICVIOiVNIiksIE5JRikgJT4lCiAgICBzdW1tYXJpc2UoCiAgICAgIGNvbnN1bXB0aW9uPW1lYW4oY29uc3VtcHRpb24sbmEucm09VCkKICAgICkgJT4lCiAgICBtdXRhdGUoCiAgICAgIHl0aW1lID0gYXMuUE9TSVhjdChwYXN0ZTAoIjIwMjAtIix5dGltZSksZm9ybWF0ID0gIiVZLSVtLSVkICVIOiVNIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICB0ej0iRXVyb3BlL01hZHJpZCIpCiAgICApICU+JQogICAgZmlsdGVyKCBpcy5maW5pdGUoeXRpbWUpICkgJT4lCiAgICB1bmdyb3VwKCkKfQpgYGAKCjxjZW50ZXI+IApgYGB7ciwgZWNobz1GLCByZXN1bHRzPVQsIHdhcm5pbmc9RiwgbWVzc2FnZT1GfQppZihleGlzdHNfZGVtYW5kX2RhdGEpIHsKICAjIFZpc3VhbGl0emVtIGxlcyBjb3JiZXMgZGUgY8OgcnJlZ2EgYWdyZWdhZGVzIGQndW4gYW55IHRpcHVzIHBlciBOSUYsIHV0aWxpdHphbnQgdW4gZ3LDoGZpYyBpbnRlcmFjdGl1CiAgIyBnZ3Bsb3RseSgKICAjICAgZ2dwbG90KGRmX2x5KSArIAogICMgICAgIGdlb21fYXJlYShhZXMoeXRpbWUsY29uc3VtcHRpb24sZmlsbD1OSUYsZ3JvdXA9Q1VQUykscG9zaXRpb249InN0YWNrIikgKwogICMgICAgIHhsYWIoInRlbXBzIikgKyB5bGFiKCJjb25zdW0gKGtXaCkiKSArIAogICMgICAgIGdndGl0bGUoIkNvcmJlcyBkZSBjw6BycmVnYSBhZ3JlZ2FkZXMgZHVyYW50IHVuIGFueSIpICsgdGhlbWVfYncoKQogICMgKQogIGRmX2x5X2Nhc3QgPC0gcGl2b3Rfd2lkZXIoCiAgICBkYXRhID0gZGZfbHksIAogICAgbmFtZXNfZnJvbSA9ICJOSUYiLCAKICAgIHZhbHVlc19mcm9tID0gImNvbnN1bXB0aW9uIikKICBwIDwtIGR5Z3JhcGgoCiAgICAgIHh0cyhkZl9seV9jYXN0ICU+JSBzZWxlY3QoLXl0aW1lKSwgb3JkZXIuYnkgPSBkZl9seV9jYXN0JHl0aW1lKSwKICAgICAgbWFpbiA9ICJDb3JiZXMgZGUgY8OgcnJlZ2EgYWdyZWdhZGVzIHBlciBOSUYgZHVyYW50IHVuIGFueSBuYXR1cmFsIiwKICAgICAgd2lkdGggPSA5MDAsCiAgICAgIGhlaWdodCA9IDUwMAogICAgKSAlPiUKICAgIGR5T3B0aW9ucyhzdGFja2VkR3JhcGggPSBUUlVFLCBjb25uZWN0U2VwYXJhdGVkUG9pbnRzID0gVCwKICAgICAgICAgICAgICBmaWxsR3JhcGg9VCwgZHJhd0dyaWQgPSBGKSAgJT4lCiAgICBkeUxlZ2VuZChzaG93ID0gImFsd2F5cyIpICU+JQogICAgZHlSYW5nZVNlbGVjdG9yKGhlaWdodCA9IDMwKSAlPiUKICAgIGR5QXhpcygieCIsIGxhYmVsID0gInRlbXBzIikgJT4lCiAgICBkeUF4aXMoInkiLCBsYWJlbCA9ICJjb25zdW0gKGtXaCkiKSAlPiUKICAgIGR5SGlnaGxpZ2h0KGhpZ2hsaWdodENpcmNsZVNpemUgPSAyLjUsCiAgICAgICAgICAgICAgICBoaWdobGlnaHRTZXJpZXNCYWNrZ3JvdW5kQWxwaGEgPSAwLjgsCiAgICAgICAgICAgICAgICBoaWRlT25Nb3VzZU91dCA9IFQpICU+JQogICAgZHlSb2xsZXIocm9sbFBlcmlvZCA9IDEpICU+JSAKICAgIGR5Q3Jvc3NoYWlyKGRpcmVjdGlvbiA9ICJ2ZXJ0aWNhbCIpCiAgaHRtbHRvb2xzOjp0YWdMaXN0KAogICAgbGlzdChodG1sdG9vbHM6OnRhZ3MkZGl2KHAsIHN0eWxlID0gInBhZGRpbmc6MTBweDsgYm9yZGVyOiBzb2xpZCIpKQogICkKfQpgYGAKPC9jZW50ZXI+CgoqKioKCiMjIyAxLjIgQWdyZWdhY2nDsyBkZSBkZW1hbmRhIG1pdGphIHBlciBlc3RhY2nDsyBkZSBsJ2FueQoKQ29tIGVzIHBvdCB2ZXVyZSwgbCdhZ3JlZ2FjacOzIGFudGVyaW9yIGVucyBwZXJtZXQgYW5hciBhIHVuIGRldGFsbCBtb2x0IHByZWPDrXMgc29icmUgcXVpbmEgZGVtYW5kYSBhZ3JlZ2FkYSBoaSBoYWd1w6lzIGhhZ3V0IGEgY2FkYSBob3JhIGkgcXVpbmEgcmVwYXJ0aWNpw7MgZW50cmUgZWxzIGRpZmVyZW50cyB1c3VhcmlzLiAKVG90IGkgYWl4w7IsIHBlciBlbnRlbmRyZSB1bmEgbWljYSBtw6lzIGVsIGRlIGVzw7NuIHVuYSBtaWNhIGNvbXBsaWNhdHMgZGUgZGlnCgoKYGBge3IsIGVjaG89VCwgcmVzdWx0cz1GLCB3YXJuaW5nPUYsIG1lc3NhZ2U9Rn0KaWYoZXhpc3RzX2RlbWFuZF9kYXRhKSB7CiAgZGZfbHlfc2Vhc29uIDwtIGRmX05JRiAlPiUKICAgIG11dGF0ZSgKICAgICAgeWRheSA9IGFzLm51bWVyaWMoZm9ybWF0KHRpbWUsIiVqIikpLAogICAgICBzZWFzb24gPSBpZmVsc2UoeWRheTw3OSB8IHlkYXk+PTM1NSwgIkhpdmVybiIsCiAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoeWRheT49NzkgJiB5ZGF5PDE3MiwgIlByaW1hdmVyYSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKHlkYXk+PTE3MiAmIHlkYXk8MjY2LCJFc3RpdSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJUYXJkb3IiKSkpLAogICAgICBtb250aCA9IG1vbnRoKHRpbWUpCiAgICApICU+JQogICAgZ3JvdXBfYnkoaG91ciA9IHNwcmludGYoIiUwMmk6JTAyaSIsaG91cih0aW1lKSxtaW51dGUodGltZSkpLCAKICAgICAgICAgICAgIHNlYXNvbiwgTklGKSAlPiUKICAgIHN1bW1hcmlzZSgKICAgICAgIyBkYXRlID0gYXMuRGF0ZShwYXN0ZTAoZm9ybWF0KHRpbWUsIiVZLSVtIiksIi0wMSIpKSwKICAgICAgZGF0ZSA9IGlmZWxzZShzZWFzb249PSJIaXZlcm4iLGFzLkRhdGUoIjIwMjEtMDItMDEiKSwKICAgICAgICAgICAgICBpZmVsc2Uoc2Vhc29uPT0iUHJpbWF2ZXJhIixhcy5EYXRlKCIyMDIxLTA1LTAxIiksCiAgICAgICAgICAgICAgIGlmZWxzZShzZWFzb249PSJFc3RpdSIsYXMuRGF0ZSgiMjAyMS0wOC0wMSIpLAogICAgICAgICAgICAgICAgaWZlbHNlKHNlYXNvbj09IlRhcmRvciIsYXMuRGF0ZSgiMjAyMS0xMS0wMSIpKSkpKSwKICAgICAgY29uc3VtcHRpb249bWVhbihjb25zdW1wdGlvbikKICAgICkgJT4lCiAgICBtdXRhdGUoCiAgICAgIHRpbWUgPSBhcy5QT1NJWGN0KHBhc3RlKGFzLkRhdGUoZGF0ZSxvcmlnaW49YXMuRGF0ZSgiMTk3MC0wMS0wMSIpKSxob3VyKSkKICAgICkgJT4lIHVuZ3JvdXAoKSAlPiUKICAgIHNlbGVjdCgtaG91ciwgLWRhdGUpCn0KYGBgCgo8Y2VudGVyPiAKYGBge3IsIGVjaG89RiwgcmVzdWx0cz1ULCB3YXJuaW5nPUYsIG1lc3NhZ2U9Rn0KaWYoZXhpc3RzX2RlbWFuZF9kYXRhKSB7CiAgZGZfbHlfY2FzdF9zZWFzb24gPC0gYXMuZGF0YS5mcmFtZSgKICAgIHBpdm90X3dpZGVyKGRmX2x5X3NlYXNvbiwgCiAgICAgIG5hbWVzX2Zyb20gPSAiTklGIiwgCiAgICAgIHZhbHVlc19mcm9tID0gImNvbnN1bXB0aW9uIix2YWx1ZXNfZm4gPSBtZWFuKSkKICBwdHMgPC0gbGFwcGx5KHVuaXF1ZShkZl9seV9jYXN0X3NlYXNvbiRzZWFzb24pLCBmdW5jdGlvbih3cyl7CiAgICBhdXggPC0gZGZfbHlfY2FzdF9zZWFzb24gJT4lIGZpbHRlcihzZWFzb249PXdzKQogICAgcCA8LSBkeWdyYXBoKAogICAgICAgIHh0cyhhdXggJT4lIHNlbGVjdCgtdGltZSwtc2Vhc29uKSwgb3JkZXIuYnkgPSBhdXgkdGltZSksCiAgICAgICAgbWFpbiA9IHNwcmludGYoIiVzIC0gQ29yYmVzIGRlIGPDoHJyZWdhIG1pdGplcyBhZ3JlZ2FkZXMgcGVyIE5JRiIsd3MpLAogICAgICAgIHdpZHRoID0gOTAwLAogICAgICAgIGhlaWdodCA9IDUwMAogICAgICApICU+JQogICAgICBkeU9wdGlvbnMoc3RhY2tlZEdyYXBoID0gVFJVRSwgY29ubmVjdFNlcGFyYXRlZFBvaW50cyA9IFQsCiAgICAgICAgICAgICAgICBmaWxsR3JhcGg9VCwgZHJhd0dyaWQgPSBGKSAgJT4lCiAgICAgIGR5TGVnZW5kKHNob3cgPSAiYWx3YXlzIikgJT4lCiAgICAgIGR5UmFuZ2VTZWxlY3RvcihoZWlnaHQgPSAzMCkgJT4lCiAgICAgIGR5QXhpcygieCIsIGxhYmVsID0gInRlbXBzIikgJT4lCiAgICAgIGR5QXhpcygieSIsIGxhYmVsID0gImNvbnN1bSAoa1doKSIpICU+JQogICAgICBkeUhpZ2hsaWdodChoaWdobGlnaHRDaXJjbGVTaXplID0gMi41LAogICAgICAgICAgICAgICAgICBoaWdobGlnaHRTZXJpZXNCYWNrZ3JvdW5kQWxwaGEgPSAwLjgsCiAgICAgICAgICAgICAgICAgIGhpZGVPbk1vdXNlT3V0ID0gVCkgJT4lCiAgICAgIGR5Um9sbGVyKHJvbGxQZXJpb2QgPSAxKSAlPiUgCiAgICAgIGR5Q3Jvc3NoYWlyKGRpcmVjdGlvbiA9ICJ2ZXJ0aWNhbCIpCiAgICAgIGh0bWx0b29sczo6dGFncyRkaXYocCwgc3R5bGUgPSAicGFkZGluZzoxMHB4OyBib3JkZXI6IHNvbGlkIikKICB9KQogIGh0bWx0b29sczo6dGFnTGlzdChwdHMpCn0KYGBgCjwvY2VudGVyPgoKKioqCgojIyMgTWFwYSBkZSBjYWxvciBhbnVhbCBkZSBsYSBkZW1hbmRhIGFncmVnYWRhCgpVbmEgbWFuZXJhIGFsdGVybmF0aXZhIGkgcXVlIGVucyBwcm9wb3JjaW9uYSBtb2x0YSBpbmZvcm1hY2nDsyBlbiB1biBjb3AgZGUgdmlzdGEgYWwgcmVzcGVjdGUgZGUgbGEgZGVtYW5kYSBlbmVyZ8OodGljYSBhZ3JlZ2FkYSBzw7NuIGVscyBtYXBlcyBkZSBjYWxvci4KCmBgYHtyfQpkZl9seV90b3RhbCA8LSBkZl9seSAlPiUgCiAgZ3JvdXBfYnkoeXRpbWUpICU+JQogIHN1bW1hcmlzZSgKICAgIGNvbnN1bXB0aW9uID0gbWVhbihjb25zdW1wdGlvbikqc3VtKGlzLmZpbml0ZShjb25zdW1wdGlvbikpCiAgKSAlPiUKICB1bmdyb3VwKCkgJT4lIAogIG11dGF0ZSh5ZWFyID0geWVhcih5dGltZSksCiAgICAgICAgIG1vbnRoID0gbW9udGgoeXRpbWUsIGxhYmVsPVRSVUUpLAogICAgICAgICBkYXkgPSBkYXkoeXRpbWUpLAogICAgICAgICBob3VyID0gaG91cih5dGltZSkpICU+JQogIHNlbGVjdCh5dGltZSxkYXksaG91cixtb250aCx5ZWFyLGNvbnN1bXB0aW9uKSAlPiUKICBmaWxsKGNvbnN1bXB0aW9uKQpgYGAKCjxjZW50ZXI+CmBgYHtyLGVjaG89RixyZXN1bHRzPVQsbWVzc2FnZT1GLHdhcm5pbmc9RixvdXQud2lkdGg9JzEwMCUnfQpwIDwtZ2dwbG90KGRmX2x5X3RvdGFsICU+JSBzZWxlY3QoLXl0aW1lKSxhZXMoZGF5LGhvdXIsZmlsbD1jb25zdW1wdGlvbikpKwogIGdlb21fdGlsZShjb2xvcj0gIndoaXRlIixzaXplPTAuMSkgKyAKICBzY2FsZV9maWxsX3ZpcmlkaXMobmFtZT0iY29uc3VtIChrV2gpIixvcHRpb24gPSJDIikKcCA8LXAgKyBmYWNldF9ncmlkKHllYXJ+bW9udGgpCnAgPC1wICsgc2NhbGVfeV9jb250aW51b3VzKHRyYW5zID0gInJldmVyc2UiLCBicmVha3MgPSB1bmlxdWUoZGZfbHlfdG90YWwkaG91cikpCnAgPC1wICsgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9YygxLDEwLDIwLDMxKSkKcCA8LXAgKyB0aGVtZV9taW5pbWFsKGJhc2Vfc2l6ZSA9IDgpCnAgPC1wICsgbGFicyh0aXRsZT0gcGFzdGUoIk1hcGEgZGUgY2Fsb3IgZGUgbGEgZGVtYW5kYSBhZ3JlZ2FkYSBlbiBhbnkgdGlwdXMiKSwgeD0iRGlhIiwgeT0iSG9yYSIpCnAgPC1wICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpKwogIHRoZW1lKHBsb3QudGl0bGU9ZWxlbWVudF90ZXh0KHNpemUgPSAxNCkpKwogIHRoZW1lKGF4aXMudGV4dC55PWVsZW1lbnRfdGV4dChzaXplPTYpKSArCiAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KHNpemU9NixhbmdsZT05MCkpICsKICB0aGVtZShzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGNvbG91cj0id2hpdGUiKSkrCiAgdGhlbWUocGxvdC50aXRsZT1lbGVtZW50X3RleHQoaGp1c3Q9MCkpKwogIHRoZW1lKGF4aXMudGlja3M9ZWxlbWVudF9ibGFuaygpKSsKICB0aGVtZShheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9NykpKwogIHRoZW1lKGxlZ2VuZC50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT04KSkrCiAgdGhlbWUobGVnZW5kLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9NikpKwogIHJlbW92ZUdyaWQoKSNnZ0V4dHJhCnAKYGBgCjwvY2VudGVyPgoKIyMgMi4gR2VuZXJhY2nDsyBmb3Rvdm9sdMOgaWNhCgpTZWdvbnMgZWxzIGVzY2VuYXJpcyBkZWZpbml0cyBlbiBlbHMgZml0eGVycyBKU09OIChkZWZpbml0LCBwZXIgZXhlbXBsZSBhOiBEYWRlcy9QVkdJUy9lc2NlbmFyaXMuanNvbiksIHMnZXN0aW1hIHF1aW5hIMOpcyBsYSBwcm9kdWNjacOzIEZvdG9Wb2x0YWljYSAoRlYpIGRlIGNhZGFzY8O6biBkZWxzIGNhbXBzIGRlZmluaXRzLiBDYWRhc2PDum4gZCdlbGxzIGFuaXLDoCBkZWZpbml0IHBlciB1biBub20gcXVlIGhhIGRlIHNlciDDum5pYy4gQSBwb3N0ZXJpb3JpLCBlbCBwcm9ncmFtYSBxdWUgZ2VuZXJhIGxhIGxsaWJyZXRhIGphIGNhbGN1bGEgcXVpbmEgw6lzIGxhIGdlbmVyYWNpw7MgYWdyZWdhZGEgZGUgdG90cyBlbHMgY2FtcHMsIMOpcyBhIGRpciwgcG9kZW4gZGVmaW5pci1zZSBkaWZlcmVudHMgdGlwb2xvZ2llcyBkZSBjYW1wcyBGViwgYW1iIGNhbnZpcyBkZSBwb3NpY2lvbnMgaS9vIGNhcmFjdGVyw61zdGlxdWVzIGVuIGNhZGFzY8O6biBkJ2VsbHMuCgpgYGB7cn0KaWYobGVuZ3RoKHB2Z2lzX2ZpbGVzKT09MCl7CiAgZ2VuZXJhdGlvbl9kZiA8LSBOVUxMCiAgIk5vIHMnaGEgZGVmaW5pdCBjYXAgY2FtcCBmb3Rvdm9sdGFpYyEgR2VuZXJldSB1biBmaXR4ZXIgSlNPTiBjb20gYSBsJ2V4ZW1wbGUiCn0gZWxzZSB7CiAgcHZnaXNfc2NlbmFyaW9zIDwtIGRvLmNhbGwoYyxsYXBwbHkocHZnaXNfZmlsZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24oeClmcm9tSlNPTih4LHNpbXBsaWZ5RGF0YUZyYW1lID0gRikpKQogIGdlbmVyYXRpb25fZGYgPC0gcHZnaXNfZG93bmxvYWRfc2NlbmFyaW9zKHB2Z2lzX3NjZW5hcmlvcykKfQpgYGAKClNlZ3VpZGFtZW50LCB2aXN1YWxpdHplbSBsYSBwcm9kdWNjacOzIEZWIGRlIGNhZGFzY8O6biBkZWxzIGNhbXBzIHF1ZSBlc3TDoW4gZGVmaW5pdHMgZW4gZWwgZml0eGVyICpEYWRlcy9QVkdJUy9lc2NlbmFyaXMuanNvbioKCjxjZW50ZXI+CmBgYHtyLCBlY2hvPUYsIHJlc3VsdHM9VCwgbWVzc2FnZT1GLCB3YXJuaW5nPUYsIG91dC53aWR0aD0iMTAwJSJ9CmlmKCFpcy5udWxsKGdlbmVyYXRpb25fZGYpKSB7CiAgcHRzIDwtIGxhcHBseSh1bmlxdWUoZ2VuZXJhdGlvbl9kZiROb21DYW1wRlYpLGZ1bmN0aW9uKGkpewogICAgYXV4IDwtIGdlbmVyYXRpb25fZGYgJT4lIGZpbHRlcihOb21DYW1wRlY9PWkpCiAgICBwIDwtIGR5Z3JhcGgoeHRzKHggPSBkYXRhLmZyYW1lKHZhbG9yPWF1eCRnZW5lcmF0aW9uKSwgCiAgICAgICAgICAgICAgICBvcmRlci5ieSA9IGF1eCR0aW1lKSwgCiAgICAgICAgICAgIG1haW49c3ByaW50ZigiTm9tQ2FtcEZWOiAlcyIsaSksCiAgICAgICAgICAgIHdpZHRoID0gODAwLCBoZWlnaHQ9MzUwKSAlPiUKICAgICAgZHlPcHRpb25zKGNvbm5lY3RTZXBhcmF0ZWRQb2ludHMgPSBULCBmaWxsR3JhcGg9VCwgZmlsbEFscGhhPTAuNCwgCiAgICAgICAgICAgICAgICBkcmF3R3JpZCA9IEYsIGNvbG9ycz0iI0Q4QUU1QSIpICAlPiUKICAgICAgZHlSYW5nZVNlbGVjdG9yKGhlaWdodCA9IDMwKSAlPiUKICAgICAgZHlBeGlzKCJ4IiwgbGFiZWwgPSAidGVtcHMiKSAlPiUKICAgICAgZHlBeGlzKCJ5IiwgbGFiZWwgPSAiZ2VuZXJhY2nDsyAoa1doKSIpICU+JQogICAgZHlIaWdobGlnaHQoaGlnaGxpZ2h0Q2lyY2xlU2l6ZSA9IDIuNSwKICAgICAgICAgICAgICAgIGhpZ2hsaWdodFNlcmllc0JhY2tncm91bmRBbHBoYSA9IDAuOCwKICAgICAgICAgICAgICAgIGhpZGVPbk1vdXNlT3V0ID0gVCkgJT4lCiAgICBkeVJvbGxlcihyb2xsUGVyaW9kID0gMSkgJT4lIAogICAgZHlDcm9zc2hhaXIoZGlyZWN0aW9uID0gInZlcnRpY2FsIikKICAgICBodG1sdG9vbHM6OnRhZ3MkZGl2KHAsIHN0eWxlID0gInBhZGRpbmc6MTBweDsgYm9yZGVyOiBzb2xpZCIpCiAgfSkKICBodG1sdG9vbHM6OnRhZ0xpc3QocHRzKQp9CmBgYAo8L2NlbnRlcj4KCgojIDMuIENvbXByb3ZhY2nDsyBkZWwgbml2ZWxsIGQnYXV0b2NvbnN1bQpgYGB7ciwgZWNobz1GLCByZXN1bHRzPUZ9CmlmKCFpcy5udWxsKGdlbmVyYXRpb25fZGYpKXsKICBkZl9seV90b3RhbF93aXRoX2dlbiA8LSBkZl9seV90b3RhbCAlPiUgCiAgICBsZWZ0X2pvaW4oZ2VuZXJhdGlvbl9kZiAlPiUgZmlsdGVyKE5vbUNhbXBGVj09IlRvdGFsRlYiKSAlPiUgc2VsZWN0KC1Ob21DYW1wRlYpLGJ5ID0gYygieXRpbWUiPSJ0aW1lIikpCiAgZGZfbHlfdG90YWxfd2l0aF9nZW4kc3VycGx1cyA8LQogICAgaWZlbHNlKGRmX2x5X3RvdGFsX3dpdGhfZ2VuJGdlbmVyYXRpb24+ZGZfbHlfdG90YWxfd2l0aF9nZW4kY29uc3VtcHRpb24sCiAgICAgICAgICAgZGZfbHlfdG90YWxfd2l0aF9nZW4kZ2VuZXJhdGlvbiAtIGRmX2x5X3RvdGFsX3dpdGhfZ2VuJGNvbnN1bXB0aW9uLDApCiAgZGZfbHlfdG90YWxfd2l0aF9nZW4kc2VsZmNvbnMgPC0gIAogICAgaWZlbHNlKGRmX2x5X3RvdGFsX3dpdGhfZ2VuJGdlbmVyYXRpb248PWRmX2x5X3RvdGFsX3dpdGhfZ2VuJGNvbnN1bXB0aW9uLCAKICAgICAgICAgICBkZl9seV90b3RhbF93aXRoX2dlbiRnZW5lcmF0aW9uLCBkZl9seV90b3RhbF93aXRoX2dlbiRjb25zdW1wdGlvbikKICBkZl9seV90b3RhbF93aXRoX2dlbiRzZWxmY29uc3BlcmMgPC0KICAgIChkZl9seV90b3RhbF93aXRoX2dlbiRzZWxmY29ucy9kZl9seV90b3RhbF93aXRoX2dlbiRjb25zdW1wdGlvbikgKiAxMDAKICBkZl9seV90b3RhbF93aXRoX2dlbiRmcm9tZ3JpZCA8LSAKICAgIC0gZGZfbHlfdG90YWxfd2l0aF9nZW4kY29uc3VtcHRpb24gKyBkZl9seV90b3RhbF93aXRoX2dlbiRzZWxmY29ucwp9CmBgYAoKYGBge3J9CmlmKCFpcy5udWxsKGdlbmVyYXRpb25fZGYpKXsKICBwIDwtIGR5Z3JhcGgoCiAgICAgICAgeHRzKGRmX2x5X3RvdGFsX3dpdGhfZ2VuICU+JSBzZWxlY3Qoc3VycGx1cywgc2VsZmNvbnMsIGZyb21ncmlkKSAlPiUKICAgICAgICAgICAgICByZW5hbWUoIkV4Y2VkZW50cyI9InN1cnBsdXMiLCJBdXRvY29uc3VtIj0ic2VsZmNvbnMiLAogICAgICAgICAgICAgICAgICAgICAiWGFyeGFFbMOoY3RyaWNhIj0iZnJvbWdyaWQiKSwgCiAgICAgICAgICAgIG9yZGVyLmJ5ID0gZGZfbHlfdG90YWxfd2l0aF9nZW4keXRpbWUpLAogICAgICAgIG1haW4gPSBwYXN0ZTAoIkV4Y2VkZW50cywgYXV0b2NvbnN1bSBzb2xhciAoIiwKICAgICAgICAgIHJvdW5kKHN1bShkZl9seV90b3RhbF93aXRoX2dlbiRzZWxmY29ucyxuYS5ybT1UKS8KICAgICAgICAgICAgICAgICAgc3VtKGRmX2x5X3RvdGFsX3dpdGhfZ2VuJGdlbmVyYXRpb24sbmEucm09VCksMykqMTAwLAogICAgICAgICAgIiUpIGkgY29uc3VtIGRlIHhhcnhhIHJlc3BlY3RlIHRvdGFsICgiLAogICAgICAgICAgcm91bmQoLXN1bShkZl9seV90b3RhbF93aXRoX2dlbiRmcm9tZ3JpZCxuYS5ybT1UKS8KICAgICAgICAgICAgICAgICAgc3VtKGRmX2x5X3RvdGFsX3dpdGhfZ2VuJGNvbnN1bXB0aW9uLG5hLnJtPVQpLDMpKjEwMCwiJSkiKSwKICAgICAgICB3aWR0aCA9IDkwMCwKICAgICAgICBoZWlnaHQgPSA1MDAKICAgICAgKSAlPiUKICAgICAgZHlPcHRpb25zKHN0YWNrZWRHcmFwaCA9IEYsIGNvbm5lY3RTZXBhcmF0ZWRQb2ludHMgPSBULAogICAgICAgICAgICAgICAgZmlsbEdyYXBoPVQsIGRyYXdHcmlkID0gRikgICU+JQogICAgICBkeUxlZ2VuZChzaG93ID0gImFsd2F5cyIpICU+JQogICAgICBkeVJhbmdlU2VsZWN0b3IoaGVpZ2h0ID0gMzApICU+JQogICAgICBkeUF4aXMoIngiLCBsYWJlbCA9ICJ0ZW1wcyIpICU+JQogICAgICBkeUF4aXMoInkiLCBsYWJlbCA9ICJlbGVjdHJpY2l0YXQgKGtXaCkiKSAlPiUKICAgICAgZHlIaWdobGlnaHQoaGlnaGxpZ2h0Q2lyY2xlU2l6ZSA9IDIuNSwKICAgICAgICAgICAgICAgICAgaGlnaGxpZ2h0U2VyaWVzQmFja2dyb3VuZEFscGhhID0gMC45LAogICAgICAgICAgICAgICAgICBoaWRlT25Nb3VzZU91dCA9IFQpICU+JQogICAgICBkeVJvbGxlcihyb2xsUGVyaW9kID0gMSkgJT4lIAogICAgICBkeUNyb3NzaGFpcihkaXJlY3Rpb24gPSAidmVydGljYWwiKQogIGh0bWx0b29sczo6dGFnTGlzdCgKICAgIGxpc3QoaHRtbHRvb2xzOjp0YWdzJGRpdihwLCBzdHlsZSA9ICJwYWRkaW5nOjEwcHg7IGJvcmRlcjogc29saWQiKSkKICApCn0KYGBgCgoK